﻿using System.Dynamic;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ApiLib.Utils.Converters;

/// <summary>
/// Json转换：【其他类型】=>【object】
/// </summary>
public class ObjectAsPrimitiveConverter : JsonConverter<object>
{
    /// <summary>
    /// Float类型转换
    /// </summary>
    public FloatFormat FloatFormat { get; init; }

    /// <summary>
    /// 未知数字格式化
    /// </summary>
    public UnknownNumberFormat UnknownNumberFormat { get; init; }

    /// <summary>
    /// 对象格式化
    /// </summary>
    public ObjectFormat ObjectFormat { get; init; }

    /// <summary>
    /// FloatFormat.Double, UnknownNumberFormat.Error, ObjectFormat.Expando
    /// </summary>
    public ObjectAsPrimitiveConverter() : this(FloatFormat.Double, UnknownNumberFormat.Error, ObjectFormat.Expando)
    {
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="floatFormat"></param>
    /// <param name="unknownNumberFormat"></param>
    /// <param name="objectFormat"></param>
    public ObjectAsPrimitiveConverter(FloatFormat floatFormat, UnknownNumberFormat unknownNumberFormat,
        ObjectFormat objectFormat)
    {
        FloatFormat = floatFormat;
        UnknownNumberFormat = unknownNumberFormat;
        ObjectFormat = objectFormat;
    }

    /// <summary>
    /// 写入
    /// </summary>
    /// <param name="writer"></param>
    /// <param name="value"></param>
    /// <param name="options"></param>
    public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
    {
        if (value.GetType() == typeof(object))
        {
            writer.WriteStartObject();
            writer.WriteEndObject();
        }
        else
        {
            JsonSerializer.Serialize(writer, value, value.GetType(), options);
        }
    }

    /// <summary>
    /// 读取
    /// </summary>
    /// <param name="reader"></param>
    /// <param name="typeToConvert"></param>
    /// <param name="options"></param>
    /// <returns></returns>
    /// <exception cref="JsonException"></exception>
    public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        switch (reader.TokenType)
        {
            case JsonTokenType.Null:
                return null;
            case JsonTokenType.False:
                return false;
            case JsonTokenType.True:
                return true;
            case JsonTokenType.String:
                return reader.GetString() ?? "";
            case JsonTokenType.Number:
            {
                if (reader.TryGetInt32(out var i))
                    return i;
                if (reader.TryGetInt64(out var l))
                    return l;
                // BigInteger could be added here.
                if (FloatFormat == FloatFormat.Decimal && reader.TryGetDecimal(out var m))
                    return m;
                if (FloatFormat == FloatFormat.Double && reader.TryGetDouble(out var d))
                    return d;
                using var doc = JsonDocument.ParseValue(ref reader);
                if (UnknownNumberFormat == UnknownNumberFormat.JsonElement)
                    return doc.RootElement.Clone();
                throw new JsonException($"Cannot parse number {doc.RootElement.ToString()}");
            }
            case JsonTokenType.StartArray:
            {
                var list = new List<object?>();
                while (reader.Read())
                {
                    switch (reader.TokenType)
                    {
                        default:
                            list.Add(Read(ref reader, typeof(object), options));
                            break;
                        case JsonTokenType.EndArray:
                            return list;
                    }
                }

                throw new JsonException();
            }
            case JsonTokenType.StartObject:
                var dict = CreateDictionary();
                while (reader.Read())
                {
                    switch (reader.TokenType)
                    {
                        case JsonTokenType.EndObject:
                            return dict;
                        case JsonTokenType.PropertyName:
                            var key = reader.GetString() ?? "";
                            reader.Read();
                            dict.Add(key, Read(ref reader, typeof(object), options));
                            break;
                        default:
                            throw new JsonException();
                    }
                }

                throw new JsonException();
            default:
                throw new JsonException($"Unknown token {reader.TokenType}");
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    private IDictionary<string, object?> CreateDictionary() =>
        ObjectFormat == ObjectFormat.Expando ? new ExpandoObject() : new Dictionary<string, object?>();
}

/// <summary>
/// 浮点类型
/// </summary>
public enum FloatFormat
{
    /// <summary>
    /// 小数
    /// </summary>
    Double,

    /// <summary>
    /// 小数，非常精确，适用于金钱
    /// </summary>
    Decimal,
}

/// <summary>
/// 位置数字类型
/// </summary>
public enum UnknownNumberFormat
{
    /// <summary>
    /// 错误
    /// </summary>
    Error,

    /// <summary>
    /// Json类型
    /// </summary>
    JsonElement,
}

/// <summary>
/// 对象格式化类型
/// </summary>
public enum ObjectFormat
{
    /// <summary>
    /// 扩展对象
    /// </summary>
    Expando,

    /// <summary>
    /// 字典
    /// </summary>
    Dictionary,
}